<?php

/**
 * 数据类型基类
 * 
 * @package modules
 * @subpackage Common
 * @author suning <snsnsky@gmail.com>
 */

namespace Common;

class DataType
{
    /**
     * 数据类型掩码
     */
    const T_MASK = 0xF0;

    // {{{
    /**
     * 数据内容: 允许为NULL
     */
    const F_NULLABLE = 0x01;
    /**
     * 数据内容: 不允许为空
     */
    const F_NONEMPTY = 0x02;
    // }}}

    // {{{
    /**
     * 数值符号: 正
     */
    const S_POSITIVE = 0x01;
    /**
     * 数值符号: 负
     */
    const S_NEGATIVE = 0x02;
    /**
     * 数值符号: 非负
     */
    const S_NONNEGATIVE = 0x04;
    // }}}

    // {{{
    /**
     * 数据类型: 数值(包括整数及小数)
     */
    const T_DECIMAL = 0x10;
    /**
     * 数据类型: 正数
     */
    const T_DECIMAL_POSITIVE = 0x11;
    /**
     * 数据类型: 负数
     */
    const T_DECIMAL_NEGATIVE = 0x12;
    /**
     * 数据类型: 非负数
     */
    const T_DECIMAL_NONNEGATIVE = 0x14;

    /**
     * 数据类型: 整数
     */
    const T_INTEGER = 0x20;
    /**
     * 数据类型: 正整数
     */
    const T_INTEGER_POSITIVE = 0x21;
    /**
     * 数据类型: 负整数
     */
    const T_INTEGER_NEGATIVE = 0x22;
    /**
     * 数据类型: 非负整数
     */
    const T_INTEGER_NONNEGATIVE = 0x24;

    /**
     * 数据类型: 字符串
     */
    const T_STRING = 0x30;
    /**
     * 数据类型: 字符串允许为NULL
     */
    const T_STRING_NULLABLE = 0x31;
    /**
     * 数据类型: 非空字符串
     */
    const T_STRING_NONEMPTY = 0x32;
    /**
     * 数据类型: 手机号码
     */
    const T_STRING_MOBILE = 0x34;

    /**
     * 数据类型: 数组
     */
    const T_ARRAY = 0x40;
    /**
     * 数据类型: 数组允许为NULL
     */
    const T_ARRAY_NULLABLE = 0x41;
    /**
     * 数据类型: 非空数组
     */
    const T_ARRAY_NONEMPTY = 0x42;

    /**
     * 数据类型: 日期
     */
    const T_DATETIME = 0x50;
    /**
     * 数据类型: 日期允许为NULL
     */
    const T_DATETIME_NULLABLE = 0x51;
    /**
     * 数据类型: 日期(不包含时分秒)
     */
    const T_DATETIME_DATE = 0x52;

    protected static $_checker_map = [
        // 数值
        self::T_DECIMAL => 'Decimal',
        // 整数
        self::T_INTEGER => 'Integer',
        // 字符串
        self::T_STRING => 'String',
        // 数组
        self::T_ARRAY => 'Array',
        // 日期
        self::T_DATETIME => 'Datetime',
    ];
    // }}}

    // {{{
    /**
     * 数据序列化方式: 逗号分隔
     * @var integer
     */
    const ARRAY_SERIALIZE_COMMA = 1;
    /**
     * 数据序列化方式: JSON
     * @var integer
     */
    const ARRAY_SERIALIZE_JSON = 2;
    /**
     * 数据序列化方式: 序列化
     * @var integer
     */
    const ARRAY_SERIALIZE_SERIALIZE = 3;
    /**
     * 数据序列化方式: TAB分隔
     * @var integer
     */
    const ARRAY_SERIALIZE_TAB = 4;

    private static $_array_serialize_map = [
        self::ARRAY_SERIALIZE_COMMA => [
            'encode' => ['func' => 'implode', 'args.pre' => [',']],
            'decode' => ['func' => 'explode', 'args.pre' => [',']],
        ],
        self::ARRAY_SERIALIZE_JSON => [
            'encode' => ['func' => 'json_encode', 'args.pre' => []],
            'decode' => ['func' => 'json_decode', 'args.pre' => [], 'args.post' => [true]],
        ],
        self::ARRAY_SERIALIZE_SERIALIZE => [
            'encode' => ['func' => 'serialize', 'args.pre' => []],
            'decode' => ['func' => 'unserialize', 'args.pre' => []],
        ],
        self::ARRAY_SERIALIZE_TAB => [
            'encode' => ['func' => 'implode', 'args.pre' => ["\t"]],
            'decode' => ['func' => 'explode', 'args.pre' => ["\t"]],
        ],
    ];
    // }}}

    private function __construct() {}

    public static function dataCheck($info, $schema)
    {
        $checker_map = self::$_checker_map;
        $pured = [];

        $valid_fields = array_intersect_key($info, $schema);

        foreach ($valid_fields as $field => $val)
        {
            $expr = (int) $schema[$field];
            $type = $expr & self::T_MASK;

            if (isset($checker_map[$type]))
            {
                $checker = $checker_map[$type];
                $method = "_check{$checker}";

                $checked = self::$method($val, $expr);
                if ($checked !== false)
                {
                    $pured[$field] = $checked;
                }
            }
        }

        return $pured;
    }

    protected static function _checkDecimal($v, $type = self::T_DECIMAL)
    {
        if ($v === null && ($type & self::F_NULLABLE))
        {
            return $v;
        }

        if (is_numeric($v))
        {
            if ($type & self::S_POSITIVE)
            {
                if ($v > 0)
                {
                    return $v;
                }
            }
            else if ($type & self::S_NONNEGATIVE)
            {
                if ($v >= 0)
                {
                    return $v;
                }
            }
            else if ($type & self::S_NEGATIVE)
            {
                if ($v < 0)
                {
                    return $v;
                }
            }
            else
            {
                return $v;
            }
        }

        return false;
    }

    protected static function _checkInteger($v, $type = self::T_INTEGER)
    {
        if ($v === null && ($type & self::F_NULLABLE))
        {
            return $v;
        }

        if (is_numeric($v) && preg_match('/^(?:-|\+)?(?:0|[1-9][0-9]*)$/', "{$v}"))
        {
            if ($type & self::S_POSITIVE)
            {
                if ($v > 0)
                {
                    return $v;
                }
            }
            else if ($type & self::S_NONNEGATIVE)
            {
                if ($v >= 0)
                {
                    return $v;
                }
            }
            else if ($type & self::S_NEGATIVE)
            {
                if ($v < 0)
                {
                    return $v;
                }
            }
            else
            {
                return $v;
            }
        }

        return false;
    }

    protected static function _checkString($v, $type = self::T_STRING)
    {
        if ($v === null && ($type & self::F_NULLABLE))
        {
            return $v;
        }

        if (is_string($v) || is_numeric($v))
        {
            $r = "{$v}";

            if ($type & self::F_NONEMPTY)
            {
                if (strlen($r) > 0)
                {
                    return $r;
                }
            }
            else
            {
                return $r;
            }
        }

        return false;
    }

    protected static function _checkArray($v, $type = self::T_ARRAY)
    {
        if ($v === null && ($type & self::F_NULLABLE))
        {
            return $v;
        }

        if (is_array($v))
        {
            if ($type & self::F_NONEMPTY)
            {
                if (count($v) > 0)
                {
                    return $v;
                }
            }
            else
            {
                return $v;
            }
        }

        return false;
    }

    public static function _checkDatetime($v, $type = self::T_DATETIME)
    {
        if ($v === null && ($type & self::F_NULLABLE))
        {
            return $v;
        }

        if (is_string($v) && preg_match('/^([1-2]\d{3}-\d{2}-\d{2}(?: \d{2}:\d{2}:\d{2})?)$/', $v, $m))
        {
            if ($type & self::T_DATETIME_DATE)
            {
                return $m[1];
            }
            else
            {
                return $v;
            }
        }

        return false;
    }

    public static function _checkDate($v, $type = self::T_DATETIME_DATE)
    {
        if ($v === null && ($type & self::F_NULLABLE)) {
            return $v;
        }

        if (is_string($v) && preg_match('/^[1-2]\d{3}-\d{2}-\d{2}$/', $v, $m)) {
            return $v;
        }
        return false;
    }

    /**
     * 编码数组字段
     *
     * @param array $data        待编码数据
     * @param array $meta        编码信息
     * @return array
     */
    public static function encodeArray(array $data, array $meta)
    {
        $pured = [];

        $data = array_intersect_key($data, $meta);
        $map = self::$_array_serialize_map;

        foreach ($data as $field => $val)
        {
            if ($val === null)
            {
                $pured[$field] = null;
            }
            else if (is_array($val))
            {
                $m = $meta[$field];

                if (empty($val))
                {
                    $pured[$field] = null;
                }
                else
                {
                    $val = array_slice($val, 0, $m['max'], true);

                    $g = $map[$m['type']]['encode'];

                    $args = $g['args.pre'];
                    $args[] = $val;

                    if (isset($g['args.post']))
                    {
                        $args = array_merge($args, $g['args.post']);
                    }

                    $pured[$field] = call_user_func_array($g['func'], $args);
                }
            }
        }

        return $pured;
    }

    /**
     * 解码数组字段
     *
     * @param array $data        待解码数据
     * @param array $meta        编码信息
     * @return array
     */
    public static function decodeArray(array $data, array $meta)
    {
        $pured = [];

        $data = array_intersect_key($data, $meta);
        $map = self::$_array_serialize_map;

        foreach ($data as $field => $val)
        {
            if (empty($val))
            {
                $pured[$field] = [];
            }
            else
            {
                $m = $meta[$field];
                $g = $map[$m['type']]['decode'];

                $args = $g['args.pre'];
                $args[] = $val;

                if (isset($g['args.post']))
                {
                    $args = array_merge($args, $g['args.post']);
                }

                $val = call_user_func_array($g['func'], $args);

                $pured[$field] = is_array($val) ? $val : [];
            }
        }

        return $pured;
    }

    public static function datetime($now = -1)
    {
        if ($now < 0) {
            date('Y-m-d H:i:s');
        }
        return date('Y-m-d H:i:s', $now);
    }

    public static function date()
    {
        return date('Y-m-d');
    }
}
